package com.netty.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

/** selector监听有事件发生才去处理 而不是等待连接获取读事件过来
 * 多路复用NIO,关键:通过selector上注册的channel及对应感兴趣的事件,
 * 当selector感知到事件发生,如果是连接事件,则相应的ServerSocketChannel去accept()接受连接
 * 如果是read等其他事件,则对应的socketChannel去执行对应的read()等操作
 * @Date 2021/8/31 16:52
 * @Author WangLinBao
 * @Description
 */
public class NioSelectorServerSocket {

    public static void main(String[] args) throws IOException {
        //创建NIO ServerSocketChannel
        ServerSocketChannel socketChannel = ServerSocketChannel.open();

        socketChannel.socket().bind(new InetSocketAddress(9000));
        //设置非阻塞
        socketChannel.configureBlocking(false);
        //打开Selector处理channel,即创建epoll
        Selector selector = Selector.open();
        //把 ServerSocketChannel注册到selector上,并且selector只对客户端的accept连接事件感兴趣
        socketChannel.register(selector, SelectionKey.OP_ACCEPT);
        System.out.println("服务启动....");
        while(true){//占用cpu
            //阻塞等待需要处理的事件发生
            selector.select();
            //获取selector中注册的全部事件的SelectedKeys实例
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while(iterator.hasNext()){
                SelectionKey selectionKey = iterator.next();
                //如果事件是连接事件
                if(selectionKey.isAcceptable()){
                    ServerSocketChannel server = (ServerSocketChannel) selectionKey.channel();

                    //非阻塞模式的accept方法不会阻塞
                    //NIO的非阻塞是由操作系统内核实现的,调用了Linux内核的accept函数
                    SocketChannel socketChannel1 = server.accept();
                    socketChannel1.configureBlocking(false);
                    //往客户端连接过来的socketChannel中注册到selector中,并对读事件感兴趣
                    socketChannel1.register(selector,SelectionKey.OP_READ);
                    System.out.println("客户端连接成功.....");
                }else if(selectionKey.isReadable()){
                    SocketChannel channel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = ByteBuffer.allocate(128);
                    int len = channel.read(byteBuffer);
                    if(len>0){
                        System.out.println("客户端发送的信息: "+ new String(byteBuffer.array()));
                    }else if(len==-1){
                        iterator.remove();
                        System.out.println("客户端连接关闭...");
                    }
                }
            }


        }
    }
}
